#include"head.h"
int send_all(int sockfd,const void *buf,size_t len){
    size_t total=0;
    const char*p=(const char*)buf;
    while(total<len){
        ssize_t n=send(sockfd,p+total,len-total,0);
        total+=n;
    }
    return 0;
}
int sendTlv(int sockfd,TLV *tlv){
    char buf[TLV_HEADER_SIZE];
    buf[0]=tlv->type;
    memcpy(buf+1,&tlv->seq,4);
    memcpy(buf+5,&tlv->length,4);
    if(send_all(sockfd,buf,sizeof(buf))!=0){
        return -1;
    }
    if(tlv->length>0&&tlv->value){
        return send_all(sockfd,tlv->value,tlv->length);
    }
    return 0;
}
int recv_all(int sockfd,void *buf,size_t len){
    size_t total=0;
    char *p=(char*)buf;
    while(total<len){
        ssize_t n=recv(sockfd,p+total,len-total,0);
        total+=n;
    }
    return 0;
}
int recvTlv(int sockfd,TLV* tlv){
    char buf[TLV_HEADER_SIZE];
    recv_all(sockfd,buf,sizeof(buf));
    tlv->type=buf[0];
    memcpy(&tlv->seq,buf+1,4);
    memcpy(&tlv->length,buf+5,4);
    if(tlv->length>0){
        recv_all(sockfd,tlv->value,tlv->length);
    }
    return 0;
}
//计算文件hash值
int hash_count(const char *filename,char *hash_str){

   SHA256_CTX ctx;
   SHA256_Init(&ctx);
   char buf[4096];
   int fd=open(filename,O_RDWR);
   while(1){
       bzero(buf,sizeof(buf));
       ssize_t sret=read(fd,buf,sizeof(buf));
       SHA256_Update(&ctx,buf,sret);
       if(sret==0){
           break;
       }
   }
   unsigned char sha[32];
   SHA256_Final(sha,&ctx);
   for(int i=0;i<32;i++){
       char temp[3]={0};
       sprintf(temp,"%02x",sha[i]);
       strcat(hash_str,temp);
   }
   return 0;
}
//上传文件
int upload_file(int sockfd,const char *local_path){
    int fd=open(local_path,O_RDONLY);
    ERROR_CHECK(fd,-1,"open");
    struct stat st;
    fstat(fd,&st);
    printf("filesize=%ld\n",st.st_size);
    off_t file_size=st.st_size;
    uint32_t net_file_size = htonl(file_size); 
    TLV tlv_start;//将文件大小存在value中发给服务端
    memset(&tlv_start, 0, sizeof(TLV));
    tlv_start.type = CMD_FILE_START;
    tlv_start.length = sizeof(net_file_size);
    memcpy(tlv_start.value, &net_file_size, sizeof(net_file_size));
    sendTlv(sockfd,&tlv_start);
    off_t total_sent=0;
    off_t offset=0;
    while(total_sent<file_size){
        ssize_t sent=sendfile(sockfd,fd,&offset,file_size-total_sent);
        total_sent+=sent;
        printf("已上传:%.2f%%\r",(double)total_sent/file_size*100);
        fflush(stdout);
    }
    char hash_str[65]={0};
    hash_count(local_path,hash_str);
    TLV end_tlv;//将hash值存在value中发送给服务端
    memset(&end_tlv, 0, sizeof(TLV));
    end_tlv.type = CMD_FILE_END;
    end_tlv.length = 65;
    memcpy(end_tlv.value,&hash_str,end_tlv.length);
    sendTlv(sockfd, &end_tlv);
    printf("\n文件上传 %s: %ld/%ld 字节\n",
           (total_sent == file_size) ? "完成" : "失败",
           total_sent, file_size);
    close(fd);
    return 0;    
}
//下载文件
int download(int sockfd, const char *local_path){
    printf("下载文件: %s\n", local_path);
    TLV file_header;
    recvTlv(sockfd,&file_header);
    if(file_header.type!=CMD_FILE_START){
        return -1;
    }
    uint32_t net_file_size;
    memcpy(&net_file_size,file_header.value,sizeof(net_file_size));
    int fd=open(local_path,O_WRONLY|O_CREAT|O_TRUNC,0666);
    ERROR_CHECK(fd,-1,"open");
    uint32_t file_size = ntohl(net_file_size); 
    printf("文件大小: %u 字节\n", file_size);
    uint64_t total_receive=0;
    char buff[4096];
    while(total_receive<file_size){
        size_t to_receive;
        size_t remaining=file_size-total_receive;
        if (remaining > sizeof(buff)) {
            to_receive = sizeof(buff);
        } else {
            to_receive = remaining;
        }
        ssize_t n=recv(sockfd,buff,to_receive,0);
        if (n <= 0) {
            if (n == 0) {
                printf("连接被关闭\n");
            } else {
                perror("接收数据失败");
            }
            break;
        }
        ssize_t bytes_written =write(fd,buff,n);
        if (bytes_written != n) {
            perror("写入文件失败");
            close(fd);
            return -1;
        }
        total_receive+=n;
        double progress = (double)total_receive / file_size * 100;
            printf("已下载: %.1f%%\r", progress);
            fflush(stdout);

    }
    TLV end_tlv;
    memset(&end_tlv, 0, sizeof(TLV));
    recvTlv(sockfd, &end_tlv);
    if(end_tlv.type==CMD_FILE_END){
        printf("\n文件下载结束\n");
    }
    char local_hash[65] = {0};
    hash_count(local_path, local_hash);
    if (strcmp(end_tlv.value, local_hash) != 0) {
       fprintf(stderr, "哈希校验失败！\n");
       return -1;
   }
   printf("校验成功\n");
    printf("\n文件下载 %s: %ld/%d 字节\n",
           (total_receive == file_size) ? "完成" : "失败",
           total_receive, file_size);
    close(fd);

    return 0;
}
//发送hash值

int check_file_hash(int sockfd,const char *filename){
   char hash_str[65]={0};
   hash_count(filename,hash_str);
   TLV tlv;
   memset(&tlv,0,sizeof(TLV));
   tlv.type=CMD_FILE_CHECK;
   tlv.length=65;
   memcpy(tlv.value,&hash_str,tlv.length);
   printf("str=%s\n",tlv.value);
   sendTlv(sockfd,&tlv);
   TLV response;
   recvTlv(sockfd,&response);
   if(response.type==CMD_FILE_EXISTS){
       printf("已有重复文件\n");
       return -1;
   }else if (response.type==CMD_RESPONSE){
       printf("无重复\n");
   }
   return 0;
}

